﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace IMGXOR
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            setmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            g_set = Graphics.FromImage(setmap);
        }
        Bitmap setmap;
        Graphics g_set;
        Bitmap srcmap;
        Bitmap dstmap;
        Bitmap bitmap;
        Graphics g_draw;
        byte[] xorvalue;

        List<Bitmap> srcoff = new List<Bitmap>();
        List<Bitmap> offlist = new List<Bitmap>();
        List<Rectangle> offRect = new List<Rectangle>();

        OpenFileDialog of = new OpenFileDialog();
        private bool selectmove;
        private Point startpoint;

        private void button1_Click(object sender, EventArgs e)
        {
            of.Filter = "*.*|*.*";
            if (of.ShowDialog() == DialogResult.OK)
            {
                var img = Image.FromFile(of.FileName);
                bitmap = new Bitmap(img.Width, img.Height);
                g_draw = Graphics.FromImage(bitmap);
                g_draw.Clear(Color.White);
                g_draw.DrawImage(img, 0, 0);
                pictureBox1.Image = bitmap;
                srcmap = new Bitmap(img.Width, img.Height);
                var g = Graphics.FromImage(srcmap);
                g.DrawImage(img, 0, 0);
            }
        }

        byte[] getImageData(Bitmap img,int w,int h)
        {
            var rect = new Rectangle(0, 0, w, h);
            var bd = img.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, img.PixelFormat);
            IntPtr ptr = bd.Scan0;
            int len = bd.Stride * bd.Height;
            byte[] data = new byte[len];
            Marshal.Copy(ptr, data, 0, len);
            img.UnlockBits(bd);
            return data;
        }

        void setImageData(Bitmap img, int w, int h,byte[] dst)
        {
            var rect = new Rectangle(0, 0, w, h);
            var bd = img.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, img.PixelFormat);
            IntPtr ptr = bd.Scan0;
            int len = bd.Stride * bd.Height;
            byte[] data = new byte[len];
            Marshal.Copy(ptr, data, 0, len);
            for (int i = 0; i < dst.Length; i++)
                data[i] = dst[i];
            Marshal.Copy(data, 0, ptr, len);
            img.UnlockBits(bd);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            
            of.Filter = "*.*|*.*";
            if (of.ShowDialog() == DialogResult.OK)
            {
                var img = Image.FromFile(of.FileName);
                Bitmap bp = dstmap = new Bitmap(img.Width, img.Height);
                Graphics gd = Graphics.FromImage(bp);
                gd.Clear(Color.White);
                gd.DrawImage(img, 0, 0);
                var dst = getImageData(bp, img.Width, img.Height);
                var src = getImageData(bitmap, img.Width, img.Height);
                for (int i = 0; i < dst.Length; i++)
                {
                    if ((i & 3) != 3)
                    {
                        src[i] ^= dst[i];
                        if (src[i] != 0)
                        {
                            src[i] = 0xff;
                            src[(i & -4)] = 0xff;
                            src[(i & -4) + 1] = 0xff;
                            src[(i & -4) + 2] = 0xff;
                            src[(i & -4) + 3] = 0xff;
                            i = ((i + 4) & -4)-1;
                        }
                    }
                    else
                    {
                        src[i] = 0xff;
                    }
                    
                }
                xorvalue = src;
                setImageData(bitmap, img.Width, img.Height, src);
                pictureBox1.Image = bitmap;
            }
        }

        Bitmap newMinMap(Bitmap src,int x,int y,int w,int h)
        {
            if (checkBox1.Checked)
            {
                int mx = int.Parse(textBox2.Text);
                int my = int.Parse(textBox3.Text);
                if (w < mx)
                {
                    x -= (int)(((float)mx - w)/2+0);
                    w = mx;
                }
                if (h < my)
                {
                    y -= (int)(((float)my - h)/2+0);
                    h = my;
                }
            }
            var bp = new Bitmap(w, h);
            var gp = Graphics.FromImage(bp);
            
            gp.DrawImage(src, -x, -y);
            return bp;
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            selectmove = true;
            startpoint = new Point(e.X, e.Y);
            
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!selectmove) return;
            if (bitmap == null) return;
            int x, y, ex, ey;
            if(e.X<startpoint.X)
            {
                x = e.X;
                ex = startpoint.X;
            }
            else
            {
                x = startpoint.X;
                ex = e.X;
            }

            if (e.Y < startpoint.Y)
            {
                y = e.Y;
                ey = startpoint.Y;
            }
            else
            {
                ey = e.Y;
                y = startpoint.Y;
            }
            g_set.Clear(Color.White);
            g_set.DrawImage(bitmap, 0, 0);
            g_set.DrawRectangle(Pens.Red, x, y, ex-x, ey-y);
            pictureBox1.Image = setmap;
        }

        void scan(int sx,int sy,int off,int slen,byte[] data)
        {
            int index = sy * slen + sx * 4;
            byte dat = data[index];
            if (polyBox2.Checked)
            {
                if (dat == 0xff)
                {
                    checkBox1.Checked = false;
                    data[index] = 0xfe;
                    var minrect = getMinpolybox(sx - off, sy - off, sx + off, sy + off, data);
                    Rectangle newrect;
                    while (true)
                    {
                        newrect = getMinpolybox(minrect.X - off, minrect.Y - off, minrect.Width + off, minrect.Height + off, data);
                        if (newrect.X == minrect.X && newrect.Y == minrect.Y && newrect.Width == minrect.Width && newrect.Height == minrect.Height) break;
                        minrect = newrect;
                    }
                    minrect = newrect;
                    Bitmap last;
                    int iw = minrect.Width - minrect.X + 1, ih = minrect.Height - minrect.Y + 1;
                    var mask = new byte[iw * ih];
                    getMinpolybox(minrect.X, minrect.Y, minrect.Width + 1, minrect.Height + 1, data, mask, true);
                    g_set.DrawRectangle(Pens.Red, minrect.X, minrect.Y, iw, ih);
                    offlist.Add(last = newMinMap(dstmap, minrect.X, minrect.Y, iw, ih));
                    offRect.Add(minrect);
                    textBox1.AppendText(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, iw, ih, offlist.Count-1));
                    var v = getImageData(last, last.Width, last.Height);
                    for (int ly = 0; ly < last.Height; ly++)
                        for (int lx = 0; lx < last.Width; lx++)
                        {
                            int px = lx + ly * last.Width;
                            v[px * 4 + 3] *= mask[px];
                        }
                    setImageData(last, last.Width, last.Height, v);
                    listBox1.Items.Add(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y, offlist.Count-1));
                    if (srccheck.Checked)
                    {
                        offlist.Add(newMinMap(srcmap, minrect.X, minrect.Y, minrect.Width - minrect.X + 1, minrect.Height - minrect.Y + 1));
                        offRect.Add(minrect);
                        listBox1.Items.Add(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y, offlist.Count-1));
                    }

                    //pictureBox1.Image = setmap;
                    //System.Threading.Thread.Sleep(1000);
                    //if (sx < minx)
                    //    minx = sx;
                    //if (sx > maxx)
                    //    maxx = sx;
                    //if (sy < miny)
                    //    miny = sy;
                    //if (sy > maxy)
                    //    maxy = sy;
                }
            }
            else if (dat != 0)
            {
                var minrect = getMinRect(sx - off, sy - off, sx + off, sy + off, data);
                Rectangle newrect;
                while (true)
                {
                    newrect = getMinRect(minrect.X - off, minrect.Y - off, minrect.Width + off, minrect.Height + off, data);
                    if (newrect.X == minrect.X && newrect.Y == minrect.Y && newrect.Width == minrect.Width && newrect.Height == minrect.Height) break;
                    minrect = newrect;
                }
                minrect = newrect;
                getMinRect(minrect.X - off, minrect.Y - off, minrect.Width + off, minrect.Height + off, data, true);
                g_set.DrawRectangle(Pens.Red, minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y);
                offlist.Add(newMinMap(dstmap, minrect.X, minrect.Y, minrect.Width - minrect.X + 1, minrect.Height - minrect.Y + 1));
                offRect.Add(minrect);
                textBox1.AppendText(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y, offlist.Count-1));
                listBox1.Items.Add(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y, offlist.Count-1));
                //ifcount++;
                if (srccheck.Checked)
                {
                    offlist.Add(newMinMap(srcmap, minrect.X, minrect.Y, minrect.Width - minrect.X + 1, minrect.Height - minrect.Y + 1));
                    offRect.Add(minrect);
                    listBox1.Items.Add(string.Format("ID:[{4}] X:{0},Y:{1},W:{2},H:{3}\r\n", minrect.X, minrect.Y, minrect.Width - minrect.X, minrect.Height - minrect.Y, offlist.Count-1));
                    //ifcount++;
                }

                //pictureBox1.Image = setmap;
                //System.Threading.Thread.Sleep(1000);
                //if (sx < minx)
                //    minx = sx;
                //if (sx > maxx)
                //    maxx = sx;
                //if (sy < miny)
                //    miny = sy;
                //if (sy > maxy)
                //    maxy = sy;
            }
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (!selectmove) return;
            if (bitmap == null) return;
            int x, y, ex, ey;
            int minx=int.MaxValue, miny=int.MaxValue, maxx=0,maxy=0;
            if (e.X < startpoint.X)
            {
                x = e.X;
                ex = startpoint.X;
            }
            else
            {
                x = startpoint.X;
                ex = e.X;
            }

            if (e.Y < startpoint.Y)
            {
                y = e.Y;
                ey = startpoint.Y;
            }
            else
            {
                ey = e.Y;
                y = startpoint.Y;
            }
            if (x < 0) x = 0;
            if (y < 0) y = 0;
            if (ex > bitmap.Width) ex = bitmap.Width - 1;
            if (ey > bitmap.Height) ey = bitmap.Height - 1;
            int slen = xorvalue.Length / bitmap.Height;
            byte[] data = new byte[xorvalue.Length];
            for (int i = 0; i < data.Length; i++)
                data[i] = xorvalue[i];
            g_set.Clear(Color.White);
            g_set.DrawImage(bitmap, 0, 0);
            int off = int.Parse(textBox4.Text) ;
            int ifcount = 0;
            if (srccheck.Checked)
                srcoff.Clear();
            offlist.Clear();
            offRect.Clear();
            listBox1.Items.Clear();
            listBox1.Items.Add("原始");

            int scmode = (LB1.Checked ? 1 : 0) + (LB2.Checked ? 2 : 0) + (LB3.Checked ? 4 : 0);
            switch (scmode)
            {
                case 0:
                    for (int sy = y; sy < ey; sy++)
                        for (int sx = x; sx < ex; sx++)
                        {
                            int index = sy * slen + sx * 4;
                            byte dat = data[index];
                            if (dat != 0)
                                scan(sx, sy, off, slen, data);
                        }
                    break;
                case 4:
                    for (int sx = x; sx < ex; sx++)
                        for (int sy = y; sy < ey; sy++)
                        {
                            int index = sy * slen + sx * 4;
                            byte dat = data[index];
                            if (dat != 0)
                                scan(sx, sy, off, slen, data);
                        }
                    break;
                case 5:
                    for (int sx = x; sx < ex; sx++)
                        for (int sy = ey - 1; sy >= y; sy--)
                        {
                            int index = sy * slen + sx * 4;
                            byte dat = data[index];
                            if (dat != 0)
                                scan(sx, sy, off, slen, data);
                        }
                    break;
            }
            //g_set.DrawRectangle(Pens.Red, minx, miny, maxx - minx, maxy - miny);
            pictureBox1.Image = setmap;
            selectmove = false;
            //textBox1.AppendText(string.Format("X:{0},Y:{1},W:{2},H:{3}\r\n", minx, miny, maxx - minx, maxy - miny));
        }

        Rectangle getMinpolybox(int x, int y, int ex, int ey, byte[] data,byte[] outmask=null, bool clear = false)
        {
            int minx = int.MaxValue, miny = int.MaxValue, maxx = 0, maxy = 0;

            if (x < 0) x = 0;
            if (y < 0) y = 0;
            if (ex > bitmap.Width) ex = bitmap.Width - 1;
            if (ey > bitmap.Height) ey = bitmap.Height - 1;
            int slen = data.Length / bitmap.Height;
            for (int sy = y; sy < ey; sy++)
                for (int sx = x; sx < ex; sx++)
                {
                    int index = sy * slen + sx * 4;
                    if (data[index] == 0xfe)
                    {
                        if (data[index + 4] == 0xff) data[index + 4] = 0xfe;
                        if (data[index + slen] == 0xff) data[index + slen] = 0xfe;
                        if (data[index - 4] == 0xff) data[index - 4] = 0xfe;
                        if (data[index - slen] == 0xff) data[index - slen] = 0xfe;
                        if (outmask != null)
                            outmask[(sy - y) * (ex - x) + (sx - x)] = 1;
                        if (sx < minx)
                            minx = sx;
                        if (sx > maxx)
                            maxx = sx;
                        if (sy < miny)
                            miny = sy;
                        if (sy > maxy)
                            maxy = sy;
                        if (clear)
                            data[index] = 0;
                    }
                }
            return new Rectangle(minx, miny, maxx, maxy);
        }

        Rectangle getMinRect(int x, int y, int ex, int ey, byte[] data, bool clear = false)
        {
            int minx = int.MaxValue, miny = int.MaxValue, maxx = 0, maxy = 0;
            
            if (x < 0) x = 0;
            if (y < 0) y = 0;
            if (ex > bitmap.Width) ex = bitmap.Width - 1;
            if (ey > bitmap.Height) ey = bitmap.Height - 1;
            int slen = data.Length / bitmap.Height;
            for (int sy = y; sy < ey; sy++)
                for (int sx = x; sx < ex; sx++)
                {
                    int index = sy * slen + sx * 4;
                    if (data[index] != 0)
                    {
                        if (sx < minx)
                            minx = sx;
                        if (sx > maxx)
                            maxx = sx;
                        if (sy < miny)
                            miny = sy;
                        if (sy > maxy)
                            maxy = sy;
                        if(clear)
                            data[index] = 0;
                    }
                }
            return new Rectangle(minx, miny, maxx, maxy);
        }

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex == 0)
            {
                g_set.Clear(Color.White);
                g_set.DrawImage(bitmap, 0, 0);
                pictureBox1.Image = setmap;
            }
            else if(listBox1.SelectedIndex != -1)
            {
                pictureBox1.Image = offlist[listBox1.SelectedIndex-1];
            }
        }

        SaveFileDialog sf = new SaveFileDialog();
        private void button4_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex > 0)
            {
                sf.Filter = "*.png|*.png";
                sf.FileName = string.Format("{0},{1}", offRect[listBox1.SelectedIndex - 1].X, offRect[listBox1.SelectedIndex - 1].Y);
                if (sf.ShowDialog() == DialogResult.OK)
                {
                    offlist[listBox1.SelectedIndex - 1].Save(sf.FileName);
                }
            }
        }

        FolderBrowserDialog fb = new FolderBrowserDialog();
        private void button3_Click(object sender, EventArgs e)
        {
            if (fb.ShowDialog() == DialogResult.OK)
            {
                for(int i = 0; i < offlist.Count; i++)
                {
                    offlist[i].Save(fb.SelectedPath+"\\" + string.Format("[{2}]{0},{1},{3},{4}", offRect[i].X, offRect[i].Y,i,offRect[i].Width- offRect[i].X+1, offRect[i].Height- offRect[i].Y+1) + ".png");
                }
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex > 0)
            {
                sf.Filter = "*.png|*.png";
                if (sf.ShowDialog() == DialogResult.OK)
                {
                    int w=offlist[0].Width, h=offlist[0].Height;
                    var bp = new Bitmap(offlist[0].Width, offlist[0].Height * offlist.Count);
                    var gr = Graphics.FromImage(bp);
                    for (int i = 0; i < offlist.Count; i++)
                    {
                        gr.DrawImage(offlist[i], 0, h * i);
                    }
                    bp.Save(sf.FileName);
                }
            }
        }
    }
}
